home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac Mania 2
/
MacMania 2.toast
/
Demo's
/
Tools&Utilities
/
Programming
/
PowerLisp 1.1
/
Library
/
compiler.lisp
< prev
next >
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
NeXTSTEP
RISC OS/Acorn
UTF-8
Wrap
Text File
|
1994-03-25
|
51.7 KB
|
2,021 lines
|
[
TEXT/ROSA
]
;;;
;;; Copyright © 1994 Roger Corman. All rights reserved.
;;;
;
; Source code for compiler.
; This is included in the "COMPILER" package.
;
(eval-when (:compile-toplevel :load-toplevel :execute)
(provide :compiler)
(in-package :compiler)
(require :assembler)
(use-package :assembler)
(export '(compiler::compile-top-level-form)))
(eval-when (:compile-toplevel :load-toplevel :execute)
(defun assembly-start (stream char)
(cons 'compiler::push-assembly-instructions (read-delimited-list #\] stream)))
(defun assembly-end (stream char) nil)
(set-macro-character #\[ #'assembly-start)
(set-macro-character #\] #'assembly-end))
;
; We do an eval-when on the entire file so that we get the
; performance benefits immediately
;
(eval-when (:compile-toplevel :load-toplevel :execute)
(defvar *assemble-code* t)
(defvar *asm* nil)
(defvar *lex-counter* 0)
(defvar *references* nil)
(defvar *function-name* nil)
(defvar *function-entry-label* nil)
(defvar *cleanup-forms-stack* nil)
(defvar *lambda-list* nil)
(defvar *arg-count* 0)
(defvar *last-call-was-values* nil)
(defvar *environment* nil)
(defvar *embedded-lambdas* nil)
(defvar *lambda-special-vars* nil)
(defvar *lambda-declarations* nil)
(defvar *lambda-special-decs* nil)
(defvar *compile-time-too-mode* nil)
(defvar *compile-print* nil)
(defvar *compile-output-file* nil)
(defvar *symbol-table* nil)
(defvar *last-call-was-tail-recursion* nil)
;; top level forms which we will output the names of while compiling
;; if *compile-print* is true
(defvar *compiler-print-forms*
'(defun defmacro defstruct defclass defvar defparameter defconstant))
(defun compile-it (name &optional lambda &aux (macro nil))
(unless (typep name 'symbol) (error "Function name expected"))
(unless lambda (setf lambda (function-definition (symbol-function name))))
(setq macro (macro-function name))
(unless (eq (car lambda) 'lambda) (error "Not a lambda expression"))
(setq *assemble-code* t)
(if macro
(setf (macro-function name) (compile-lambda lambda name))
(setf (symbol-function name) (compile-lambda lambda name)))
name)
(defun compile-without-assembling-it (name &optional lambda &aux (macro nil))
(unless (typep name 'symbol) (error "Function name expected"))
(unless lambda (setf lambda (function-definition (symbol-function name))))
(setq macro (macro-function name))
(unless (eq (car lambda) 'lambda) (error "Not a lambda expression"))
(setq *assemble-code* nil)
(compile-lambda lambda name))
(defun compile-the-file (input-file output-file print)
(setq *assemble-code* t)
(do* ((infile (open input-file))
(*compile-output-file* (progn (delete-file output-file) (open output-file)))
(*compile-print* print)
(*package* *package*)
(*readtable* *readtable*)
(*symbol-table* (make-hash-table :size 500))
(input-expression (read infile) (read infile))
code
return-value)
((eq input-expression 'Eof)
(close infile)
(set-file-type *compile-output-file* "FASL")
(close *compile-output-file*)
output-file)
(process-top-level-forms (list input-expression))))
;;
;; The following logic is taken from CLTL2 pp.90-91
;;
(defun process-top-level-forms (forms &aux code return-value print-form)
(dolist (f forms)
(setq print-form nil)
(if (not (consp f)) (go continue)) ;; no need to process non-list forms
(if (and *compile-print*
(member (car f) *compiler-print-forms*)
(consp (cdr f)))
(setq print-form (list (car f) (cadr f) "...")))
(if (macro-function (car f)) ;; if it is a macro expand it
(progn
(setq f (macroexpand f))
(if (not (consp f)) (go continue)))) ;; no need to process non-list forms
;; watch for some special forms
(if (special-form-p (car f))
(progn
;; if a progn or locally special form, recurse
(if (or (eq (car f) 'common-lisp::progn)
(eq (car f) 'common-lisp::locally))
(progn
(process-top-level-forms (cdr f))
(go continue)))
;; if compiler-let, macrolet or symbol-macrolet
(if (or (eq (car f) 'common-lisp::compiler-let)
(eq (car f) 'common-lisp::macrolet)
(eq (car f) 'common-lisp::symbol-macrolet))
(progn
(error "Compiler does not support special form: ~A" (car f))
(process-top-level-forms (cdr f))
(go continue)))
;; if eval-when
(if (eq (car f) 'common-lisp::eval-when)
(progn
(compile-top-level-eval-when-form f)
(go continue)))))
;; else it is not a special case
;; now compile it
(setq code (compile-top-level-form f))
(%write-code-to-stream code *compile-output-file* *symbol-table*)
;; evaluate the form if compile-time-too mode
(if *compile-time-too-mode*
(setq return-value (funcall code)))
continue
(if print-form
(progn
(format t "~A~%" print-form)
(file-flush)))))
(defun compile-top-level-eval-when-form (form)
(if (or (not (consp form)) (< (length form) 2) (not (listp (cadr form))))
(error "'eval-when' form missing condition list."))
(let* ((conditions (cadr form))
(load-condition
(or (member 'common-lisp::load conditions)
(member :load-toplevel conditions)))
(eval-condition
(or (member 'common-lisp::eval conditions)
(member :execute conditions)))
(compile-condition
(or (member 'common-lisp::compile conditions)
(member :compile-toplevel conditions))))
(if load-condition
(if (or compile-condition
(and *compile-time-too-mode* eval-condition))
(let ((*compile-time-too-mode* t))
(process-top-level-forms (cddr form)))
(let ((*compile-time-too-mode* nil))
(process-top-level-forms (cddr form))))
;; load not specified
(if (or compile-condition
(and *compile-time-too-mode* eval-condition))
(eval form)))))
;;
;; The cleanup forms stack needs to be maintained for use in non-local
;; lexically scoped exit situations. Specifically, GO with a target outside
;; the current construct, and RETURN-FROM when exiting an external construct.
;; Note that THROW targets are dynamic, not lexical, and therefore cannot
;; be handled at compile time. They are handled via a different mechanism, a
; run-time stack. Lexically scoped exits are better handled at compile time,
;; both for efficiency (a big concern, because GO is the primary iteration
;; facility) and because the lexical scoping is currently only known at
;; compile-time. In other words, a run-time lexical environment is not maintained
;; for compiled code, and for efficiency reasons it would be better not to have
;; to.
;;
;; Entries on the cleanup forms stack include:
;;
;; (BLOCK block-name block-exit-label)
;; (TAGBODY (local-tag-1 . local-label-1) (local-tag-2 . local-label-2) ...)
;; (LET (local-var-1 . index1) (local-var-2 . index2) ...)
;; (the LET form is used by both LET *and* LET* forms)
;; (CATCH catch-tag)
;; (UNWIND-PROTECT <compiled code to be included>)
;;
(defconstant *lambda-list-keywords*
'( &optional
&rest
&key
&aux
&allow-other-keys
&whole
&body ))
;; the following aren't allowed in lambda function declarations
;; (only in macros, which will be expanded before we see them)
(defconstant *unsupported-lambda-list-keywords*
'( &allow-other-keys
&whole
&body ))
;;
;; Set up square braces as assembly delimiters for this module
;; This helps to clearly distinguish the generated code from the
;; surrounding stuff.
;;
(defun push-assembly-instructions (&rest instructions)
(dolist (x instructions)
(push x *asm*)))
(defun push-cleanup (x) (push x *cleanup-forms-stack*))
(defun pop-cleanup () (pop *cleanup-forms-stack*))
;; We use the following registers:
;; A0, D0 : scratch registers. D0 ultimately returns the value.
;; D3 : stores last returned value
;; A2 : used as local index for function call
;; A3 : points to lexical storage for the function
;; A4 : points to function's environment (variables with indefinite extent)
;; A6 : links previous stack frame
;; A7 : stack pointer
;; A5 : global variables
;;
;; We do not need to save A5, A6 or A7
;; We also don't need to save scratch register D0.
;; We *do* need to save A0, A2, A3 and D3.
;;
;;
;; compile-top-level-form (form &optional (assemble t))
;; Given an arbitrary lisp form, returns a compiled function
;; equivalent to it.
;;
(defun compile-top-level-form (form)
(let* (
;; Establish local bindings of these special variables
;; so that this function can be entered recursively.
;;
(*asm* nil)
(*lex-counter* 0)
(*references* nil)
(*function-entry-label* (gensym))
(*last-call-was-values* nil)
(*cleanup-forms-stack* nil)
(*environment* nil)
(*embedded-lambdas* (find-lambdas form)))
;; emit code for function prolog
;; [ `(link a6 ,(- (* numargs 4))) ] ;; this is added at end
(emit-prolog)
;; compile the form
(compile-form form)
;; make sure bogus multiple values don't get returned
(unless *last-call-was-values* (kill-multiple-values))
(emit-epilog)
;; if we don't want to assemble it, exit here
(if *assemble-code*
(return (assemble *asm* *references* nil))
(return *asm*))))
;;---------------------------------------------------
;;
;; compile-lambda (lambda)
;; Given a lambda expression, returns a compiled function.
;;
(defun compile-lambda (lambda func-name)
(check-lambda lambda) ;; make sure we can compile it
(let* ((*asm* nil)
(*references* nil)
(*function-name* func-name)
(*function-entry-label* (gensym))
(*cleanup-forms-stack* nil)
(*lambda-list* (cadr lambda))
(*last-call-was-values* nil)
(*environment* *environment*) ;; inherit from enclosing expression
(*embedded-lambdas* (find-lambdas (cdr lambda)))
(*arg-count* 0)
(*lex-counter* 0)
(*lambda-special-vars* nil)
(*lambda-declarations* nil)
(*lambda-special-decs* nil)
(*last-call-was-tail-recursion* nil)
(forms (cddr lambda))
(new-vars (collect-new-vars *lambda-list*))
(lex-vars nil)
(aux-args (aux-arguments *lambda-list*)))
;; look for declarations
(do ((f forms (cdr f)))
((null f) (setq forms f))
(if (and (consp (car f)) (eq (caar f) 'declare))
(push (car f) *lambda-declarations*)
(progn (setq forms f) (return))))
;; search declarations for special declarations
(dolist (declaration *lambda-declarations*)
(dolist (dec-form (cdr declaration))
(if (and (consp dec-form) (eq (car dec-form) 'special))
(setq *lambda-special-decs*
(append (cdr dec-form) *lambda-special-decs*)))))
(setq lex-vars
(remove-if
#'(lambda (x)
(or (member x *lambda-special-decs*)
(special-variable-p x)))
new-vars
:key #'car))
(add-lexical-variables lex-vars)
(emit-prolog)
(compile-lambda-args)
(create-runtime-bindings) ;; create necessary heap bindings
;; handle aux variables by just adding an implicit let* form
(if aux-args
(setf forms `((let* ,aux-args ,@forms))))
(compile-nil) ;; store NIL as default return value
(if *lambda-special-vars*
(compile-unwind-protect-form
`(unwind-protect
(block ,func-name ,@forms)
($pop-special-bindings ',*lambda-special-vars*)))
;; else execute the forms directly
;; compile the forms as a block
(compile-block-form `(block ,func-name ,@forms)))
;; eliminate tail recursion
(if *last-call-was-tail-recursion*
(let* ((num-call-instructions (- (length *asm*) (length *last-call-was-tail-recursion*)))
(call-instructions (reverse (subseq *asm* 0 num-call-instructions)))
(find-top-label (gensym))
(copy-label))
;; strip off the function call
(setq *asm* *last-call-was-tail-recursion*)
;; push all instructions up to the bsr
(do ((inst (pop call-instructions) (pop call-instructions)))
((or (null call-instructions)
(and (consp inst) (eq (car inst) 'assembler::bsr))))
(push inst *asm*))
;; move passed params to outer stack frame
;; add return address and branch instruction to simulate jsr
[
`(move.l a7 a3)
;; position a3 above top of parameter frame
find-top-label
`(tst.l (a3+))
`(bne ,find-top-label)
;; copy parameters
copy-label
`(move.l (-a3) (-a2))
`(move.l a3 d0) ;; haven't implemented cmpa.l instruction yet
`(cmp.l a7 d0)
`(bne ,copy-label)
`(unlk a6)
`(move.l (a7) a0) ; get return address in a0
`(lea (a2 4) a7)
`(move.l a7 (-a7))
`(move.l a0 (-a7))
`(bra ,*function-entry-label*)
]
;; add the rest of the instructions
(do ((inst (pop call-instructions) (pop call-instructions)))
((null call-instructions))
(push inst *asm*))))
;; make sure bogus multiple values don't get returned
(unless *last-call-was-values* (kill-multiple-values))
(emit-epilog)
(pop-cleanup)
(if *assemble-code*
(return (assemble *asm* *references* nil))
(return *asm*))))
(defun compile-lambda-args ()
(compile-lambda-required-args)
(compile-lambda-optional-args)
(compile-lambda-rest-args)
(check-no-more-args)
(compile-lambda-key-args))
(defun collect-new-vars (lambda-list)
(let ((new-vars nil)(supplied_p_vars nil))
(dolist (n lambda-list) ;; add lexical vars
(if (not (member n *lambda-list-keywords*))
(progn
(if (consp n)
(progn
(if (>= (length n) 3) ;; get supplied_p symbols
(push (caddr n) supplied_p_vars))
(push (cons (car n) *lex-counter*) new-vars))
(push (cons n *lex-counter*) new-vars))
(incf *lex-counter*))))
(dolist (n supplied_p_vars)
(push (cons n *lex-counter*) new-vars) ;; these need to go on the end
(incf *lex-counter*))
(nreverse new-vars)))
;; emit code for start of function
(defun emit-prolog ()
[
`(movem.l a0 a2 a3 a4 d3 (-a7))
]
(if (or *embedded-lambdas* *environment*)
[
`(bsr 2) ; push current pc on stack
`(move.l (a7+) a4) ; a4 = pc
`(move.l (a4 -16) a4) ; a4 = pointer to environment (just before code)
])
[
`(movea.l (a6 8) a2) ; a2 = a6 + 8 = parameter block
`(lea (a7 20) a3) ; a3 = pointer to local arguments
; the offset to a7 should be 4 * number of
; registers saved!
])
;; emit code for end of function
(defun emit-epilog ()
[
`(move.l d3 d0)
`(movem.l (a6 ,(- -20 (* *lex-counter* 4))) a0 a2 a3 a4 d3)
`(unlk a6) ; unlink frame pointer
`(rts) ; d0 already contains return value
]
(setq *asm* (nreverse *asm*))
;; These last instructions get pushed onto the beginning
;; of the (now-reversed) instructions. Therefore they are reversed
;; here to come out in the right order.
[
`(link a6 ,(- (* *lex-counter* 4)))
*function-entry-label*
]
)
;; Make sure there are no more arguments.
(defun check-no-more-args ()
(if (not (or (rest-arguments *lambda-list*) (key-arguments *lambda-list*)))
[
`(move.l (a2+) (-a7)) ; get argument
`(jsr #'common-lisp::%checkNull) ; signal error if extra argument
`(lea (a7 4) a7) ; cleanup stack
]))
;;
;; compile-lambda-required-args
;; Generates code to initialize required argumensts.
;;
(defun compile-lambda-required-args ()
(dolist (sym (required-arguments *lambda-list*))
[
`(move.l (a2+) (-a7)) ; get argument
`(jsr #'common-lisp::%checkObj) ; signal error if argument missing
`(lea (a7 4) a7) ; cleanup stack
`(move.l d0 (a3 ,(* *arg-count* 4)))
]
(if (or (special-variable-p sym) (member sym *lambda-special-decs*))
(progn
(push sym *lambda-special-vars*)
[
`(move.l 0 (-a7))
`(move.l (a3 ,(* *arg-count* 4)) (-a7))
`(move.l ',sym (-a7))
`(move.l a7 (-a7))
`(jsr #'common-lisp::$push-special-bindings)
`(lea (a7 16) a7)
]))
(incf *arg-count*)))
;;
;; compile-lambda-optional-args
;; Generates code to initialize optional argumensts.
;;
(defun compile-lambda-optional-args ()
(dolist (sym (optional-arguments *lambda-list*))
;; initialize optional variable
(let ((else-label (gensym))
(end-label (gensym)))
[
`(tst.l (a2)) ;; is there an argument
`(beq ,else-label)
]
(if (and (consp sym) (>= (length sym) 3))
(compile-form `(setq ,(caddr sym) t))) ;; set supplied_p
[
`(move.l (a2+) (a3 ,(* *arg-count* 4)))
`(bra ,end-label)
else-label
]
;; else do default initialization
(if (and (consp sym) (>= (length sym) 3))
(compile-form `(setq ,(caddr sym) nil))) ;; set supplied_p
(if (and (consp sym) (cdr sym))
(progn
[
`(movem.l a2 a3 d0 (-a7))
]
(compile-form (cadr sym))
[
`(movem.l (a7+) a2 a3 d0)
`(move.l d3 (a3 ,(* *arg-count* 4)))
])
;; else
[
`(move.l 'nil (a3 ,(* *arg-count* 4)))
])
[
end-label
])
(if (or (special-variable-p sym) (member sym *lambda-special-decs*))
(progn
(push sym *lambda-special-vars*)
[
`(move.l 0 (-a7))
`(move.l (a3 ,(* *arg-count* 4)) (-a7))
`(move.l ',sym (-a7))
`(move.l a7 (-a7))
`(jsr #'common-lisp::$push-special-bindings)
`(lea (a7 16) a7)
]))
(incf *arg-count*)))
;;
;; compile-lambda-rest-args
;; Generates code to initialize rest arguments.
;; We allow more than one.
;;
(defun compile-lambda-rest-args ()
(let* ((rest-args (rest-arguments *lambda-list*)))
(if rest-args
[
`(move.l a2 (-a7))
`(jsr #'list)
`(lea (a7 4) a7)
])
(dolist (sym rest-args)
[
`(move.l d0 (a3 ,(* *arg-count* 4)))
]
(if (or (special-variable-p sym) (member sym *lambda-special-decs*))
(progn
(push sym *lambda-special-vars*)
[
`(move.l 0 (-a7))
`(move.l (a3 ,(* *arg-count* 4)) (-a7))
`(move.l ',sym (-a7))
`(move.l a7 (-a7))
`(jsr #'common-lisp::$push-special-bindings)
`(lea (a7 16) a7)
]))
(incf *arg-count*))))
;;
;; compile-lambda-key-args
;; Generates code to initialize key argumensts.
;;
(defun compile-lambda-key-args ()
(dolist (n (key-arguments *lambda-list*))
(let* ((loop-label (gensym))
(exit-label (gensym))
(not-found-label (gensym))
lex-var
default-init
key-symbol)
(if (consp n)
(setq lex-var (car n))
(setq lex-var n))
(if (and (consp n) (cdr n))
(setq default-init (cadr n))
(setq default-init nil))
(setq key-symbol
(intern (symbol-name lex-var) (find-package :keyword)))
[
`(move.l a2 a0) ; a0 = current argument location
`(move.l ',key-symbol d0)
loop-label
`(tst.l (a0)) ; make sure there are more arguments
`(beq ,not-found-label)
`(cmp.l (a0+) d0)
`(bne ,loop-label)
`(move.l (a0) (-a7)) ; make sure there is another argument
`(jsr #'common-lisp::%checkObj)
`(lea (a7 4) a7) ; cleanup stack
`(move.l d0 (a3 ,(* *arg-count* 4)))
`(bra ,exit-label)
not-found-label
]
(compile-form default-init)
[
`(move.l d3 (a3 ,(* *arg-count* 4)))
exit-label
]
(if (or (special-variable-p n) (member n *lambda-special-decs*))
(progn
(push n *lambda-special-vars*)
[
`(move.l 0 (-a7))
`(move.l (a3 ,(* *arg-count* 4)) (-a7))
`(move.l ',n (-a7))
`(move.l a7 (-a7))
`(jsr #'common-lisp::$push-special-bindings)
`(lea (a7 16) a7)
]))
(incf *arg-count*))))
;;---------------------------------------------------
(defun compile-form (form)
(setq *last-call-was-values* nil)
(setq *last-call-was-tail-recursion* nil)
(cond
((null form) (compile-nil))
((symbolp form) (compile-symbol form))
((not (consp form)) (compile-literal-form form))
(t (compile-list-form form))))
(defun compile-list-form (form)
(let ((firstobj (car form)))
(cond
((consp firstobj) (compile-explicit-lambda form))
((not (symbolp firstobj))
(error "Can't compile form--does not begin with a symbol"))
((macro-function firstobj) (compile-form (macroexpand form)))
((special-form-p firstobj) (compile-special-form form))
((eq firstobj 'common-lisp::values) (compile-values-form form))
(t (compile-function-call-form form)))))
(defun compile-special-form (form)
(case (car form)
(quote (compile-quote-form form))
(if (compile-if-form form))
(tagbody (compile-tagbody-form form))
(go (compile-go-tag form))
(setq (compile-setq-form form))
(block (compile-block-form form))
(return-from (compile-return-from-form form))
(progn (compile-progn-form form))
(let (compile-let-form form))
(let* (compile-let*-form form))
(flet (compile-flet-form form))
(labels (compile-labels-form form))
(function (compile-function-special-form form))
(catch (compile-catch-form form))
(throw (compile-throw-form form))
(unwind-protect (compile-unwind-protect-form form))
(multiple-value-call (compile-multiple-value-call-form form))
(eval-when (compile-eval-when-form form))
(declare nil)
(otherwise (error "Special form not supported: ~A~%" (car form)))))
(defun compile-explicit-lambda (form)
(if (not (eq 'lambda (caar form)))
(error "The first element of the expression: ~A is a list but it
isn't a lambda expression~%" (car form)))
(compile-form `(funcall (function ,(car form)) ,@(cdr form))))
(defun compile-symbol (sym)
(let ((temp (find-lex sym))) ; check for lexical variable
(if temp
(if (integerp (cdr temp))
[
`(move.l (a3 ,(* (cdr temp) 4)) d3)
]
;; else
[
`(move.l (a3 ,(* (cadr temp) 4)) a0)
`($CDR a0 d3)
])
;; else see if it is in the inherited environment
(if (member sym *environment*)
[
`(move.l 0 (-a7))
`(move.l ',sym (-a7))
`(move.l a4 (-a7))
`(move.l a7 (-a7))
`(jsr #'%environment-get-value)
`(lea (a7 16) a7)
`(move.l d0 d3)
]
;; else assume special variable
(compile-function-call-form `(symbol-value ',sym))))))
(defun compile-if-form (form)
(let ((else-label (gensym))
(end-label (gensym))
(test-form (cadr form))
(then-form (caddr form))
(else-form (cdddr form)))
(compile-form test-form)
[
`(cmp.l 'nil d3)
`(beq ,else-label)
]
(compile-form then-form)
(if (consp else-form)
[
`(bra ,end-label)
])
[
else-label
]
(if (consp else-form)
(compile-form (car else-form)))
[
end-label
]))
(defun compile-tagbody-form (form)
(let ((tags nil))
;; go through list once collecting tags
(dolist (n (cdr form))
(if (or (integerp n) (symbolp n))
(push (cons n (gensym)) tags)))
(push-cleanup (cons 'tagbody tags))
(dolist (n (cdr form))
(if (or (integerp n) (symbolp n))
(push (cdr (assoc n tags)) *asm*)
;; else it is a form to be evaluated
(compile-form n)))
(pop-cleanup)))
(defun compile-go-tag (form)
(let ((tag (cadr form)))
(if (not (or (integerp tag) (symbolp tag)))
(error "Invalid go tag encountered"))
(if (not (find-go-tag tag)) ;; if the tag is not already defined
(error "Tag not defined in this scope"))
;; peel off cleanup stack
(let ((dest (find-go-tag-tagbody tag)))
(dolist (f *cleanup-forms-stack*)
(if (eq f dest) (return)) ;; returns from the dolist block
(case (car f)
(unwind-protect
;; include cleanup code
(let ((cleanup-code (cdr f)))
(dolist (n cleanup-code)
(push n *asm*))))
(catch
;; remove dynamic catch tag
[
`(jsr #'common-lisp::%popCatcher) ;; restore result
]))))
[
`(bra ,(cdr (find-go-tag tag)))
]))
(defun compile-setq-form (form)
(do ((f (cdr form) (cddr f)) var val temp)
((endp f))
(setq var (car f))
(setq val (cadr f))
(setf temp (find-lex var)) ; check for lexical variable
(if temp
(progn
(compile-form val)
(if (integerp (cdr temp))
[
`(move.l d3 (a3 ,(* (cdr temp) 4)))
]
;; else
[
`(move.l (a3 ,(* (cadr temp) 4)) a0)
`($SETCDR a0 d3)
]))
;; else look in the inherited environment
(if (member var *environment*)
(progn
(compile-form val)
[
`(move.l 0 (-a7))
`(move.l d3 (-a7))
`(move.l ',var (-a7))
`(move.l a4 (-a7))
`(move.l a7 (-a7))
`(jsr #'%environment-set-value)
`(lea (a7 20) a7)
`(move.l d0 d3)
])
;; else call set function
(compile-form `(set ',var ,val))))))
(defun compile-quote-form (form)
(compile-literal-form (cadr form)))
(defun compile-block-form (form)
(let ((block-name (cadr form))
(block-forms (cddr form))
(exit-label (gensym)))
(push-cleanup (list 'block block-name exit-label))
(dolist (f block-forms)
(compile-form f))
[
exit-label
]
(pop-cleanup)))
(defun compile-return-from-form (form)
(let ((block-name (cadr form))
(retval nil)
temp)
(if (consp (cddr form))
(setq retval (caddr form)))
(compile-form retval)
(if (null block-name)
(setq temp (find-any-block))
;; else
(setq temp (find-block block-name)))
(unless temp (error "Block label not found"))
;; peel off cleanup stack
(let ((dest temp))
(dolist (f *cleanup-forms-stack*)
(if (eq f dest) (return)) ;; returns from the dolist block
(case (car f)
(unwind-protect
;; include cleanup code
(let ((cleanup-code (cdr f)))
(dolist (n cleanup-code)
(push n *asm*))))
(catch
;; remove dynamic catch tag
[
`(jsr #'common-lisp::%popCatcher) ;; restore result
]))))
[
`(bra ,(caddr temp))
]))
(defun compile-progn-form (form)
(let ((progn-forms (cdr form)))
(dolist (f progn-forms)
(compile-form f))))
(defun compile-let-form (form)
(let* ((local-vars (cadr form))
(let-forms (cddr form))
(new-vars nil)
(special-vars nil)
(declarations nil)
(special-decs nil)
sym)
;; look for declarations
(do ((f let-forms (cdr f)))
((null f) (setq let-forms f))
(if (and (consp (car f)) (eq (caar f) 'declare))
(push (car f) declarations)
(progn (setq let-forms f) (return))))
;; search declarations for special declarations
(dolist (declaration declarations)
(dolist (dec-form (cdr declaration))
(if (and (consp dec-form) (eq (car dec-form) 'special))
(setq special-decs (append (cdr dec-form) special-decs)))))
;; go through variable list evaluating values and assigning to temporary
;; space on the stack
(dolist (f local-vars)
(unless (or (consp f) (symbolp f))
(error "Invalid 'let' variable"))
(if (or (symbolp f) (not (consp (cdr f))))
[
`(move.l 'nil (a3 ,(* *lex-counter* 4)))
]
;; else
(progn
(compile-form (cadr f))
[
`(move.l d3 (a3 ,(* *lex-counter* 4)))
]))
;; add the symbol to the list of new symbols
(if (consp f)
(setq sym (car f))
(setq sym f))
(if (or (special-variable-p sym) (member sym special-decs))
(progn
(if (null special-vars) ;; if first one
[
`(move.l 0 (-a7))
])
(push sym special-vars)
[
`(move.l (a3 ,(* *lex-counter* 4)) (-a7))
`(move.l ',sym (-a7))
])
;; else
(push (cons sym *lex-counter*) new-vars))
(incf *lex-counter*))
;; add the new variables to the lexical environment
(add-lexical-variables new-vars)
(create-runtime-bindings)
;; if any special variables are present, add those bindings now
(if special-vars
(progn
[
`(move.l a7 (-a7))
`(jsr #'common-lisp::$push-special-bindings)
`(lea (a7 ,(* 8 (1+ (length special-vars)))) a7)
]
(compile-unwind-protect-form
`(unwind-protect
(progn ,@let-forms)
($pop-special-bindings ',special-vars))))
;; else execute the forms directly
(dolist (f let-forms)
(compile-form f)))
;; restore old lexical environment
(pop-cleanup)))
(defun compile-let*-form (form)
(let* ((local-vars (cadr form))
(let-forms (cddr form))
(special-vars nil)
(declarations nil)
(special-decs nil)
sym
(lex-var-count 0))
;; look for declarations
(do ((f let-forms (cdr f)))
((null f) (setq let-forms f))
(if (and (consp (car f)) (eq (caar f) 'declare))
(push (car f) declarations)
(progn (setq let-forms f) (return))))
;; search declarations for special declarations
(dolist (declaration declarations)
(dolist (dec-form (cdr declaration))
(if (and (consp dec-form) (eq (car dec-form) 'special))
(setq special-decs (append (cdr dec-form) special-decs)))))
;; go through variable list evaluating values and assigning to temporary
;; space on the stack
(dolist (f local-vars)
(unless (or (consp f) (symbolp f))
(error "Invalid 'let' variable: ~A~%" f))
(if (or (symbolp f) (not (consp (cdr f))))
[
`(move.l 'nil (a3 ,(* *lex-counter* 4)))
]
;; else
(progn
(compile-form (cadr f))
[
`(move.l d3 (a3 ,(* *lex-counter* 4)))
]))
;; add the symbol to the list of new symbols
(if (consp f)
(setq sym (car f))
(setq sym f))
(if (or (special-variable-p sym) (member sym special-decs))
(progn
(push sym special-vars)
[
`(move.l 0 (-a7))
`(move.l (a3 ,(* *lex-counter* 4)) (-a7))
`(move.l ',sym (-a7))
`(move.l a7 (-a7))
`(jsr #'common-lisp::$push-special-bindings)
`(lea (a7 16) a7)
])
;; else
(progn
(add-lexical-variables (list (cons sym *lex-counter*)))
(incf lex-var-count)))
(incf *lex-counter*))
(create-runtime-bindings)
;; if any special variables are present, add those bindings now
(if special-vars
(compile-unwind-protect-form
`(unwind-protect
(progn ,@let-forms)
($pop-special-bindings ',special-vars)))
;; else execute the forms directly
(dolist (f let-forms)
(compile-form f)))
;; restore old lexical environment
(dotimes (i lex-var-count)
(pop-cleanup))))
(defun compile-flet-form (form)
(let* ((local-funs (cadr form))
(flet-forms (cddr form))
(new-funs nil)
(declarations nil))
;; look for declarations
(do ((f flet-forms (cdr f)))
((null f) (setq flet-forms f))
(if (and (consp (car f)) (eq (caar f) 'declare))
(push (car f) declarations)
(progn (setq flet-forms f) (return))))
;; search declarations for special declarations
#|
;; do we need to deal with special declarations here? RGC
(dolist (declaration declarations)
(dolist (dec-form (cdr declaration))
(if (and (consp dec-form) (eq (car dec-form) 'special))
(setq special-decs (append (cdr dec-form) special-decs)))))
|#
;; go through function list evaluating values and assigning to temporary
;; space on the stack
(dolist (f local-funs)
(unless (and (consp f) (consp (cdr f)))
(error "Invalid 'flet' function expression"))
(let* ((func-name (car f))
(func-args (cadr f))
(func-forms (cddr f)))
(compile-function-special-form
`(function (lambda ,func-args (block ,func-name ,@func-forms))))
[
`(move.l d3 (a3 ,(* *lex-counter* 4)))
]
;; add the function name to the list of new functions
(push (cons func-name *lex-counter*) new-funs)
(incf *lex-counter*)))
;; add the new functions to the lexical environment
(add-lexical-functions new-funs)
(create-runtime-bindings)
;; execute the forms directly
(dolist (f flet-forms)
(compile-form f))
;; restore old lexical environment
(pop-cleanup)))
(defun compile-labels-form (form)
(let* ((local-funs (cadr form))
(flet-forms (cddr form))
(new-funs nil)
(declarations nil))
;; look for declarations
(do ((f flet-forms (cdr f)))
((null f) (setq flet-forms f))
(if (and (consp (car f)) (eq (caar f) 'declare))
(push (car f) declarations)
(progn (setq flet-forms f) (return))))
;; search declarations for special declarations
#|
;; do we need to deal with special declarations here? RGC
(dolist (declaration declarations)
(dolist (dec-form (cdr declaration))
(if (and (consp dec-form) (eq (car dec-form) 'special))
(setq special-decs (append (cdr dec-form) special-decs)))))
|#
(let ((counter *lex-counter*))
(dolist (f local-funs)
(unless (and (consp f) (consp (cdr f)))
(error "Invalid 'labels' function expression"))
(let* ((func-name (car f)))
(push (cons func-name counter) new-funs)
(incf counter))))
;; add the new functions to the lexical environment
(add-lexical-functions new-funs)
(create-runtime-bindings)
;; go through function list evaluating values and assigning to temporary
;; space on the stack
(dolist (f local-funs)
(let* ((func-name (car f))
(func-args (cadr f))
(func-forms (cddr f)))
(compile-function-special-form
`(function (lambda ,func-args (block ,func-name ,@func-forms))))
[
`(move.l d3 (a3 ,(* *lex-counter* 4)))
]
(incf *lex-counter*)))
;; execute the forms directly
(dolist (f flet-forms)
(compile-form f))
;; restore old lexical environment
(pop-cleanup)))
(defun compile-function-special-form (form)
(let ((func-form (cadr form)))
;; I don't think this will occur, but just in case, we can't
;; keep a reference to an anonymous function object.
(if (functionp func-form)
(error "Can't compile expression with anonymous function: ~A~%" form))
;; if a compiled lambda expression
(if (and (consp func-form) (eq (car func-form) 'lambda))
(let ((name nil)
(first-form (third func-form)))
(if (and (consp first-form) (eq (first first-form) 'block))
(setq name (second (third func-form))))
;; create a new compiled function
(setq func-form (compile-lambda func-form name))
[
`(move.l 0 (-a7))
`(move.l ',func-form (-a7))
`(move.l a7 (-a7))
`(jsr #'%copy-compiled-function)
`(lea (a7 12) a7)
`(move.l d0 d3)
]
(export-environment)
(return)))
(unless (symbolp func-form)
(error "function special form: ~%Expected a symbol: ~A~%" func-form))
(let ((temp (find-lex-function func-form))) ; check for lexical function
(if temp
(if (integerp (cdr temp))
[
`(move.l (a3 ,(* (cdr temp) 4)) d3)
]
;; else
[
`(move.l (a3 ,(* (cadr temp) 4)) a0)
`($CDR a0 d3)
])
;; else see if it is in the inherited environment
(if (member func-form *environment*)
(progn
[
`(move.l 0 (-a7))
`(move.l ',func-form (-a7))
`(move.l a4 (-a7))
`(move.l a7 (-a7))
`(jsr #'%environment-get-function)
`(lea (a7 16) a7)
`(move.l d0 d3)
])
;; else assume global function
(compile-function-call-form `(symbol-function ',func-form)))))))
(defun compile-catch-form (form)
(let ((catch-tag (cadr form))
(catch-forms (cddr form))
(exit-label (gensym)))
(push-cleanup (list 'CATCH catch-tag))
;; evaluate the tag
(compile-form catch-tag)
;; make room for jmp-buf on stack (12 * 4 bytes)
[
`(lea (a7 -48) a7)
;; pushCatcher(tag, jmp_buf)
`(move.l a7 (-a7)) ;; push jmp_buf
`(move.l d3 (-a7)) ;; push tag
`(jsr #'common-lisp::%pushCatcher)
`(lea (a7 8) a7) ;; cleanup stack
;; setjmp(jmp_buf)
`(move.l a7 (-a7)) ;; push jmp_buf
`(jsr #'common-lisp::%setjmp)
`(lea (a7 4) a7)
;; if d0 != 0, we caught an exception
`(move.l d0 d3)
`(tst.l d0)
`(bne ,exit-label)
`(move.l 'nil d3)
]
(dolist (f catch-forms)
(compile-form f))
[
exit-label
]
(pop-cleanup)
;; popCatcher()
[
`(lea (a7 48) a7) ;; cleanup jmp_buf
`(jsr #'common-lisp::%popCatcher)
]))
(defun compile-throw-form (form)
(let ((throw-tag (cadr form))
(throw-form (caddr form)))
;; evaluate the form
(compile-form throw-form)
[
`(move.l d3 (-a7))
]
;; evaluate the tag
(compile-form throw-tag)
[
`(move.l d3 (-a7))
`(jsr #'%throwException) ;; call throw handler
]))
(defun compile-unwind-protect-form (form)
(let ((protected-form (cadr form))
(cleanup-forms (cddr form))
(label1 (gensym))
(label2 (gensym)))
;; make room for jmp-buf on stack (12 * 4 bytes)
[
`(lea (a7 -48) a7)
;; pushCatcher(tag, jmp_buf)
`(move.l a7 (-a7)) ;; push jmp_buf
`(moveq 0 d0)
`(move.l d0 (-a7)) ;; push tag
`(jsr #'common-lisp::%pushCatcher)
`(lea (a7 8) a7) ;; cleanup stack
;; setjmp(jmp_buf)
`(move.l a7 (-a7)) ;; push jmp_buf
`(jsr #'common-lisp::%setjmp)
`(lea (a7 4) a7)
;; if d0 != 0, we caught an exception
`(move.l d0 d3)
`(move.l d0 (-a7)) ;; save result on stack
`(tst.l d0)
`(bne ,label1)
]
;; generate code for cleanup forms
(let ((*asm* nil))
[
`(move.l d3 (-a7)) ;; store result
`(move.l common-lisp::%multiple-values-address a0)
`(move.l (a0) (-a7))
`(jsr #'common-lisp::%popCatcher)
]
(dolist (f cleanup-forms)
(compile-form f))
[
`(move.l common-lisp::%multiple-values-address a0)
`(move.l (a7+) (a0))
`(move.l (a7+) d3) ;; retrieve result
]
(setq *asm* (nreverse *asm*))
(push-cleanup (cons 'UNWIND-PROTECT *asm*)))
;; compile protected form
(compile-form protected-form)
[
label1
]
;; include cleanup code
(let ((cleanup-code (cdr (pop-cleanup))))
(dolist (n cleanup-code)
(push n *asm*)))
;; retrieve exception result
[
`(move.l (a7+) a0)
`(tst.l a0)
`(beq ,label2)
;; continue thrown exception
`(move.l a0 (-a7))
`(jsr #'common-lisp::%continueException)
label2
`(lea (a7 48) a7) ;; cleanup jmp_buf
]))
;; for non toplevel eval-when forms
(defun compile-eval-when-form (form)
(if (or (not (consp form)) (< (length form) 2) (not (listp (cadr form))))
(error "'eval-when' form missing condition list."))
(let* ((conditions (cadr form)))
(if (or (member 'common-lisp::eval conditions)
(member :execute conditions))
(compile-progn-form (cons 'common-lisp::progn (cddr form)))
(compile-nil))))
(defun compile-multiple-value-call-form (form)
(let* ((func (cadr form))
(forms (cddr form))
(numforms (length forms))
(stackframe (* 4 (1+ numforms)))
(counter 0)
temp)
(compile-form func)
[
`(move.l d3 (-a7)) ; push function address on stack
`(lea (a7 ,(- stackframe)) a7)
]
(dolist (p forms) ; execute each form
(compile-form p)
[
`($IFELSE
(
(tst.l (common-lisp::%multiple-values-address))
)
(
;; if no multiple values, just list the single value
(move.l 0 (-a7))
(move.l 'nil (-a7))
(move.l d3 (-a7))
(move.l a7 (-a7))
(jsr #'cons)
(lea (a7 16) a7)
(move.l d0 d3)
)
(
;; otherwise get the list of values
(move.l (common-lisp::%multiple-values-address) d3)
))
`(move.l d3 (a7 ,(* counter 4)))
]
(incf counter))
;; concatenate all the lists together and store in d3
[
`(clr.l (a7 ,(* counter 4)))
`(move.l a7 (-a7)) ; pass address of params to function
`(jsr #'append)
`(move.l d0 d3)
`(lea (a7 ,(+ 4 stackframe)) a7)
]
;; now apply the passed function to the resulting value list
[
`(move.l (a7+) a0) ; a0 = function address
`(move.l 0 (-a7))
`(move.l d3 (-a7)) ; argument list
`(move.l a0 (-a7)) ; function
`(move.l a7 (-a7)) ; pass address of params to function
`(jsr #'apply)
`(move.l d0 d3)
`(lea (a7 16) a7)
]))
(defun compile-values-form (form)
(compile-function-call-form form)
(setq *last-call-was-values* t))
(defun compile-function-call-form (form)
#|
;; print warning message if function hasn't been defined yet
(if (not (functionp (symbol-function (car form))))
(format t "Warning: function ~A missing definition~%" (car form)))
|#
(if (or (find-lex-function (car form)) (member (car form) *environment*))
(progn
(compile-function-call-form `(funcall (function ,(car form)) ,@(cdr form)))
(return)))
(let* ((numparams (1- (length form)))
(stackframe (* 4 (1+ numparams)))
(func (car form))
(funcparams (cdr form))
(counter 0)
(tail-recursive (if (eq func *function-name*) *asm*))
temp)
[
`(lea (a7 ,(- stackframe)) a7)
]
(dolist (p funcparams) ; get parameters for function call
(setf temp (find-lex p)) ; check for lexical variable
(if temp
(if (integerp (cdr temp))
[
`(move.l (a3 ,(* (cdr temp) 4)) (a7 ,(* counter 4)))
]
;; else
[
`(move.l (a3 ,(* (cadr temp) 4)) a0)
`($CDR a0 (a7 ,(* counter 4)))
])
;; else
(progn
(compile-form p) ; ignore multiple values in params
[
`(move.l d3 (a7 ,(* counter 4)))
]))
(incf counter))
;; clear the last position to zero
[
`(clr.l (a7 ,(* counter 4)))
`(move.l a7 (-a7)) ; pass address of params to function
]
;; if it is a recursive call to this function, we need to handle it specially
(if (eq func *function-name*)
[
`(bsr ,*function-entry-label*)
]
;; else
(progn
[
`(jsr #',func)
]))
[
`(move.l d0 d3)
`(lea (a7 ,(+ 4 stackframe)) a7) ;; clean up stack
]
;; flag tail recursion
(setq *last-call-was-tail-recursion* tail-recursive)))
(defun compile-integer (form)
(if (typep form 'bignum)
(compile-bignum form)
[
`(move.l ,form (-a7))
`(jsr #'common-lisp::%integerAtom)
`(lea (a7 4) a7)
`(move.l d0 d3)
]))
(defun compile-bignum (num)
(let* ((numcells (cl::%bignum-cells num))
(length-flag (if (minusp num) (- numcells) numcells)))
;; allocate room for the data
[
`(lea (a7 ,(- (* (1+ numcells) 4))) a7)
`(move.l a7 a0)
`(move.l ,length-flag (a0+))
]
(dotimes (i numcells)
[
`(move.l ,(cl::%bignum-cell num i) (a0+))
])
;; now push the address of this data on the stack and create a bignum
[
`(move.l a7 (-a7))
`(jsr #'cl::%bignumAtomFromLongs)
`(lea (a7 ,(+ 8 (* 4 numcells))) a7)
`(move.l d0 d3)
]))
(defun string-int-with-pad (string index)
(if (>= index (length string))
0
(char-int (elt string index))))
(defun compile-string (string)
(let* ((numchars (+ 1 (length string)))
n
temp
(num-longs (truncate (+ 3 numchars) 4)))
;; allocate room for the string
[
`(lea (a7 ,(- (* num-longs 4))) a7)
`(move.l a7 a0)
]
(dotimes (i num-longs)
(setq temp (* i 4))
;; gather four characters into a long
(setq n
(+
(* (string-int-with-pad string temp) #x1000000)
(* (string-int-with-pad string (+ temp 1)) #x10000)
(* (string-int-with-pad string (+ temp 2)) #x100)
(string-int-with-pad string (+ temp 3))))
[
`(move.l ,n (a0+))
])
;; now push the address of this string on the stack and create a string
[
`(move.l a7 (-a7))
`(jsr #'common-lisp::%stringAtom)
`(lea (a7 ,(+ 4 (* 4 num-longs))) a7)
`(move.l d0 d3)
]))
;; need to add support for bit-vectors
(defun compile-literal-form (form)
(cond
((symbolp form) [ `(move.l ',form d3) ])
((integerp form) (compile-integer form))
((stringp form) (compile-string form))
((characterp form) (compile-character form))
((listp form) (compile-quoted-list form))
((vectorp form) (compile-vector form))
((floatp form) (compile-float form))
((typep form 'ratio)(compile-ratio form))
((typep form 'complex)(compile-complex form))
;; we will have to code a direct reference to the object
;; This won't work if we use 'compile-file'.
(t [ `(move.l ',form d3) ])))
(defun compile-character (form)
[
`(move.l ,(char-int form) (-a7))
`(jsr #'common-lisp::%charAtom)
`(lea (a7 4) a7)
`(move.l d0 d3)
])
;;
;; compile-quoted-list()
;; We catch and save the last form in case we are dealing with
;; a dotted list or dot pair.
;;
(defun compile-quoted-list (form &aux (last-element (cdr (last form))))
(setq form (reverse form))
(let ((list-length (length form)))
[
`(move.l 0 (-a7))
]
(compile-literal-form last-element)
[
`(move.l d3 (-a7))
]
(dolist (f form)
(compile-literal-form f)
[
`(move.l d3 (-a7))
])
[
`(move.l a7 (-a7))
`(jsr #'list*)
`(lea (a7 ,(+ 12 (* list-length 4))) a7)
`(move.l d0 d3)
]))
;;
;; compile-vector()
;;
(defun compile-vector (form)
(setq form (nreverse (concatenate 'list form)))
(let ((list-length (length form)))
[
`(move.l 0 (-a7))
]
(dolist (f form)
(compile-literal-form f)
[
`(move.l d3 (-a7))
])
[
`(move.l a7 (-a7))
`(jsr #'vector)
`(lea (a7 ,(+ 8 (* list-length 4))) a7)
`(move.l d0 d3)
]))
;; define these in order to get at the binary representation of a floating
;; point number so that we can generate the machine code to build it.
;; These functions don't check their type, so we get get the data.
(defasm car_ (x)
#{
($FUNC-BEGIN 0)
(move.l (a0) a0)
($CAR a0)
(move.l a0 (-a7))
(jsr #'common-lisp::%integerAtom)
(lea (a7 4) a7)
($RETURN d0)
})
(defasm cdr_ (x)
#{
($FUNC-BEGIN 0)
(move.l (a0) a0)
($CDR a0)
(move.l a0 (-a7))
(jsr #'common-lisp::%integerAtom)
(lea (a7 4) a7)
($RETURN d0)
})
;;
;; compile-float()
;;
(defun compile-float (form)
[
`(move.l ,(cdr_ form) (-a7))
`(move.l ,(car_ form) (-a7))
`(jsr #'common-lisp::%floatAtomFromLongs)
`(lea (a7 8) a7)
`(move.l d0 d3)
])
;;
;; compile-ratio()
;;
(defun compile-ratio (form)
[
`(move.l 0 (-a7))
]
(compile-form (denominator form))
[
`(move.l d3 (-a7))
]
(compile-form (numerator form))
[
`(move.l d3 (-a7))
`(move.l a7 (-a7))
`(jsr #'/)
`(lea (a7 16) a7)
`(move.l d0 d3)
])
;;
;; compile-complex()
;;
(defun compile-complex (form)
[
`(move.l 0 (-a7))
]
(compile-form (imagpart form))
[
`(move.l d3 (-a7))
]
(compile-form (realpart form))
[
`(move.l d3 (-a7))
`(move.l a7 (-a7))
`(jsr #'complex)
`(lea (a7 16) a7)
`(move.l d0 d3)
])
(defun check-lambda (lambda)
(let ((lambda-list (cadr lambda)))
(dolist (n lambda-list)
(if (member n *unsupported-lambda-list-keywords*)
(error "Can't compile this lambda list keyword: ~A~%" n)))))
(defun find-lex (var)
(let (found)
(dolist (n *cleanup-forms-stack* nil)
(if (eq (car n) 'LET)
(progn
(setq found (assoc var (cdr n)))
(if found (return-from find-lex found)))))))
(defun find-lex-function (var)
(let (found)
(dolist (n *cleanup-forms-stack* nil)
(if (eq (car n) 'FLET)
(progn
(setq found (assoc var (cdr n)))
(if found (return-from find-lex-function found)))))))
(defun find-go-tag (var)
(let (found)
(dolist (n *cleanup-forms-stack* nil)
(if (eq (car n) 'TAGBODY)
(progn
(setq found (assoc var (cdr n)))
(if found (return-from find-go-tag found)))))))
;;
;; find-go-tag-tagbody
;; Returns the cleanup form for the TAGBODY block which contains the
;; passed tag.
;;
(defun find-go-tag-tagbody (var)
(let (found)
(dolist (n *cleanup-forms-stack* nil)
(if (eq (car n) 'TAGBODY)
(progn
(setq found (assoc var (cdr n)))
(if found (return-from find-go-tag-tagbody n)))))))
(defun find-block (name)
(dolist (n *cleanup-forms-stack* nil)
(if (eq (car n) 'BLOCK)
(if (eq (cadr n) name)
(return-from find-block n)))))
(defun find-any-block ()
(dolist (n *cleanup-forms-stack* nil)
(if (eq (car n) 'BLOCK)
(return-from find-any-block n))))
;;
;; required-arguments
;; Returns a list of the required arguments in a lambda list.
;;
(defun required-arguments (lambda-list)
(let ((arglist nil))
(dolist (n lambda-list)
(if (member n *lambda-list-keywords*)
(return) ;; exit dolist loop
(push n arglist)))
(nreverse arglist)))
;;
;; optional-arguments
;; Returns a list of the optional arguments in a lambda list.
;;
(defun optional-arguments (lambda-list)
(let ((arglist nil))
(dolist (n (cdr (member '&optional lambda-list)))
(if (member n *lambda-list-keywords*)
(return) ;; exit dolist loop
(push n arglist)))
(nreverse arglist)))
;; we don't need this
;;
;;(defun get-supplied-p-args (lambda-list)
;; (let ((args nil) (forms (optional-arguments lambda-list)))
;; (dolist (f forms)
;; (if (>= (length f) 3)
;; (push (list (caddr f) nil) args)))
;; (reverse args)))
;;
;; rest-arguments
;; Returns a list of the rest arguments in a lambda list.
;;
(defun rest-arguments (lambda-list)
(let ((arglist nil))
(dolist (n (cdr (member '&rest lambda-list)))
(if (member n *lambda-list-keywords*)
(return) ;; exit dolist loop
(push n arglist)))
(nreverse arglist)))
;;
;; key-arguments
;; Returns a list of the optional key in a lambda list.
;;
(defun key-arguments (lambda-list)
(let ((arglist nil))
(dolist (n (cdr (member '&key lambda-list)))
(if (member n *lambda-list-keywords*)
(return) ;; exit dolist loop
(push n arglist)))
(nreverse arglist)))
;;
;; aux-arguments
;; Returns a list of the aux arguments in a lambda list.
;;
(defun aux-arguments (lambda-list)
(let ((arglist nil))
(dolist (n (cdr (member '&aux lambda-list)))
(if (member n *lambda-list-keywords*)
(return) ;; exit dolist loop
(push n arglist)))
(nreverse arglist)))
;;
;; kill-multiple-values
;; Use this function to make sure that ignored multiple values don't stick
;; around through successive evaluations.
;;
(defun kill-multiple-values ()
[
`(clr.l (common-lisp::%multiple-values-address))
])
(defun compile-nil ()
[ `(move.l 'nil d3) ]
(setq *last-call-was-values* nil))
(defun valid-lambda (x)
(and (listp x) (> (length x) 2) (eq (car x) 'lambda) (listp (cadr x))))
(defun find-lambdas (x)
(cond ((not (consp x)) nil)
((valid-lambda x) (list x))
((eq (car x) 'FLET) (cdr x))
((eq (car x) 'LABELS) (cdr x))
((eq (car x) 'DEFUN) (list x))
((eq (car x) 'DEFMACRO) (list x))
(t (append (find-lambdas (car x)) (find-lambdas (cdr x))))))
(defun add-lexical-variables (varlist)
(push-cleanup (cons 'LET varlist)))
(defun add-lexical-functions (varlist)
(push-cleanup (cons 'FLET varlist)))
(defun search-lambdas (var lambdas)
(cond ((null lambdas) nil)
((eq var lambdas) var)
((atom lambdas) nil)
((search-lambdas var (car lambdas)))
((search-lambdas var (cdr lambdas)))))
(defun referenced-by-embedded-lambdas (var)
(search-lambdas var *embedded-lambdas*))
(defun create-runtime-bindings ()
(if *embedded-lambdas*
(dolist (n *cleanup-forms-stack*)
(if (or (eq 'LET (car n)) (eq 'FLET (car n)))
(dolist (m (cdr n))
(let* ((sym (car m))
(index (cdr m)))
(if (and (integerp index)
(referenced-by-embedded-lambdas sym))
(progn
(setf (cdr m) (list index))
(push sym *environment*)
[
;; add a heap binding for the variable
`(move.l 0 (-a7))
`(move.l (a3 ,(* index 4)) (-a7))
`(move.l ',sym (-a7))
`(move.l a7 (-a7))
`(jsr #'cons)
`(lea (a7 16) a7)
`(move.l d0 (a3 ,(* index 4)))
]))))))))
;;
;; export-environment()
;; d3 points to the function to receive the environment
;;
(defun export-environment ()
;; first copy our heap environment
[
`(move.l 0 (-a7))
`(move.l a4 (-a7)) ;; our environment
`(move.l d3 (-a7)) ;; target function
`(move.l a7 (-a7))
`(jsr #'%function-environment) ;; copy it
`(lea (a7 16) a7)
;; now get the target environment in d0
`(move.l 0 (-a7))
`(move.l d3 (-a7)) ;; target function
`(move.l a7 (-a7))
`(jsr #'%function-environment) ;; get its environment
`(lea (a7 12) a7)
]
;; now add all our current heap bindings
(if *embedded-lambdas*
(dolist (n *cleanup-forms-stack*)
(if (eq 'LET (car n))
(dolist (m (cdr n))
(let* ((sym (car m))
(index (cdr m)))
(if (consp index)
[
;; add the binding to the target environment
`(move.l d0 (-a7))
`(move.l 0 (-a7))
`(move.l (a3 ,(* (car index) 4)) (-a7))
`(move.l d0 (-a7))
`(move.l a7 (-a7))
`(jsr #'%environment-add-binding)
`(lea (a7 16) a7)
`(move.l (a7+) d0)
]))))))
(if *embedded-lambdas*
(dolist (n *cleanup-forms-stack*)
(if (eq 'FLET (car n))
(dolist (m (cdr n))
(let* ((sym (car m))
(index (cdr m)))
(if (consp index)
[
;; add the binding to the target environment
`(move.l d0 (-a7))
`(move.l 0 (-a7))
`(move.l (a3 ,(* (car index) 4)) (-a7))
`(move.l d0 (-a7))
`(move.l a7 (-a7))
`(jsr #'%environment-add-function-binding)
`(lea (a7 16) a7)
`(move.l (a7+) d0)
])))))))
) ;; close beginning eval-when